/**
 * IDCardCleaner - 身份证清洗/解析工具
 * 校验身份证有效性，提取性别、出生日期、籍贯等信息
 */

// 省份代码表
const PROVINCES = {
    '11': '北京市', '12': '天津市', '13': '河北省', '14': '山西省', '15': '内蒙古自治区',
    '21': '辽宁省', '22': '吉林省', '23': '黑龙江省',
    '31': '上海市', '32': '江苏省', '33': '浙江省', '34': '安徽省', '35': '福建省', '36': '江西省', '37': '山东省',
    '41': '河南省', '42': '湖北省', '43': '湖南省', '44': '广东省', '45': '广西壮族自治区', '46': '海南省',
    '50': '重庆市', '51': '四川省', '52': '贵州省', '53': '云南省', '54': '西藏自治区',
    '61': '陕西省', '62': '甘肃省', '63': '青海省', '64': '宁夏回族自治区', '65': '新疆维吾尔自治区',
    '71': '台湾省', '81': '香港特别行政区', '82': '澳门特别行政区'
};

// 校验码权重
const WEIGHTS = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
// 校验码对照表
const CHECK_CODES = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];

class IDCardCleaner {
    /**
     * 清洗身份证号码（去除空格等）
     * @param {string} value - 输入值
     * @returns {string|null} 清洗后的身份证号（大写），无效返回null
     */
    static clean(value) {
        if (!value || typeof value !== 'string') {
            return null;
        }

        // 去除空格，转大写
        const cleaned = value.replace(/\s/g, '').toUpperCase();

        // 15位老身份证转18位
        if (/^\d{15}$/.test(cleaned)) {
            return this._convert15To18(cleaned);
        }

        // 18位身份证验证
        if (/^\d{17}[\dX]$/.test(cleaned)) {
            if (this._validateChecksum(cleaned)) {
                return cleaned;
            }
        }

        return null;
    }

    /**
     * 验证身份证是否有效
     * @param {string} value - 身份证号
     * @returns {boolean}
     */
    static validate(value) {
        return this.clean(value) !== null;
    }

    /**
     * 解析身份证信息
     * @param {string} value - 身份证号
     * @returns {Object} 解析结果
     */
    static parse(value) {
        const cleaned = this.clean(value);

        if (!cleaned) {
            return {
                valid: false,
                original: value,
                idCard: null,
                gender: null,
                birthday: null,
                age: null,
                province: null,
                provinceCode: null
            };
        }

        // 提取省份代码
        const provinceCode = cleaned.substring(0, 2);
        const province = PROVINCES[provinceCode] || '未知';

        // 提取出生日期
        const year = parseInt(cleaned.substring(6, 10));
        const month = parseInt(cleaned.substring(10, 12));
        const day = parseInt(cleaned.substring(12, 14));
        const birthday = `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`;

        // 计算年龄
        const today = new Date();
        let age = today.getFullYear() - year;
        const monthDiff = today.getMonth() + 1 - month;
        if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < day)) {
            age--;
        }

        // 提取性别（第17位，奇数男，偶数女）
        const genderCode = parseInt(cleaned.substring(16, 17));
        const gender = genderCode % 2 === 1 ? '男' : '女';

        return {
            valid: true,
            original: value,
            idCard: cleaned,
            gender,
            birthday,
            age,
            province,
            provinceCode,
            // 额外信息
            birthYear: year,
            birthMonth: month,
            birthDay: day
        };
    }

    /**
     * 批量解析身份证
     * @param {string[]} values - 身份证号数组
     * @returns {Object[]} 解析结果数组
     */
    static parseMany(values) {
        return values.map(v => this.parse(v));
    }

    /**
     * 掩码处理身份证（隐私保护）
     * @param {string} value - 身份证号
     * @param {Object} options - 配置选项
     * @returns {string} 掩码后的身份证号
     */
    static mask(value, options = {}) {
        const {
            showFirst = 6,  // 显示前几位
            showLast = 4,   // 显示后几位
            maskChar = '*'  // 掩码字符
        } = options;

        const cleaned = this.clean(value);
        if (!cleaned) {
            return value; // 无效身份证原样返回
        }

        const maskLength = cleaned.length - showFirst - showLast;
        if (maskLength <= 0) {
            return cleaned;
        }

        return cleaned.substring(0, showFirst)
            + maskChar.repeat(maskLength)
            + cleaned.substring(cleaned.length - showLast);
    }

    /**
     * 验证18位身份证校验码
     */
    static _validateChecksum(idCard) {
        let sum = 0;
        for (let i = 0; i < 17; i++) {
            sum += parseInt(idCard[i]) * WEIGHTS[i];
        }
        const checkCode = CHECK_CODES[sum % 11];
        return idCard[17] === checkCode;
    }

    /**
     * 15位身份证转18位
     */
    static _convert15To18(idCard15) {
        // 在第6位后插入19（表示19xx年）
        const idCard17 = idCard15.substring(0, 6) + '19' + idCard15.substring(6);

        // 计算校验码
        let sum = 0;
        for (let i = 0; i < 17; i++) {
            sum += parseInt(idCard17[i]) * WEIGHTS[i];
        }
        const checkCode = CHECK_CODES[sum % 11];

        return idCard17 + checkCode;
    }

    /**
     * 生成随机身份证号（仅用于测试）
     * @param {Object} options - 配置选项
     * @returns {string}
     */
    static generateRandom(options = {}) {
        const {
            province = '11',  // 默认北京
            gender = Math.random() > 0.5 ? 'male' : 'female',
            birthYear = 1990
        } = options;

        // 地区码（使用省份码+0000作为市县码）
        const areaCode = province + '0101';

        // 出生日期
        const month = String(Math.floor(Math.random() * 12) + 1).padStart(2, '0');
        const day = String(Math.floor(Math.random() * 28) + 1).padStart(2, '0');
        const birthDate = `${birthYear}${month}${day}`;

        // 顺序码（3位，奇数男，偶数女）
        let seq = Math.floor(Math.random() * 999) + 1;
        if (gender === 'male' && seq % 2 === 0) seq++;
        if (gender === 'female' && seq % 2 === 1) seq++;
        const seqCode = String(seq).padStart(3, '0');

        const idCard17 = areaCode + birthDate + seqCode;

        // 计算校验码
        let sum = 0;
        for (let i = 0; i < 17; i++) {
            sum += parseInt(idCard17[i]) * WEIGHTS[i];
        }
        const checkCode = CHECK_CODES[sum % 11];

        return idCard17 + checkCode;
    }
}

module.exports = IDCardCleaner;
